package com.swxy.club.service.code;


import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.swxy.club.dto.Activity;
import com.swxy.club.dto.CheckinCode;
import com.swxy.club.dto.CheckinRecord;
import com.swxy.club.dto.bo.CheckinBo;
import com.swxy.club.dto.bo.CodeAddBo;
import com.swxy.club.dto.bo.CurrentUserBO;
import com.swxy.club.enums.CheckinStatus;
import com.swxy.club.exceptions.CheckinCodeException;
import com.swxy.club.mapper.CheckinCodeMapper;
import com.swxy.club.service.record.CheckinRecordService;
import com.swxy.club.service.activity.ActivityService;
import com.swxy.club.utils.UserContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.time.LocalTime;


/**
 * @author 易
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class CheckinCodeServiceImpl extends ServiceImpl<CheckinCodeMapper, CheckinCode> implements CheckinCodeService{


    private final ActivityService activityService;
    private final CheckinRecordService checkinRecordService;
    final int MAX_RETRY = 6;

    @Override
    public String generateCheckinCode(CodeAddBo codeAddBo) {

        CurrentUserBO currentUser = UserContext.getCurrentUser();
        Assert.notNull(currentUser, "用户未登入");

        Activity activity = activityService.getById(codeAddBo.getActivityId());
        Assert.notNull(activity, "活动不存在");

        //签到码的有效时间必须在活动时间内
        LocalDateTime activityStart = activity.getStartTime();
        LocalDateTime activityEnd = activity.getEndTime();
        LocalTime validStart = codeAddBo.getValidStart();
        LocalTime validEnd = codeAddBo.getValidEnd();

        // 将签到码的时间附加到活动的日期上
        LocalDateTime codeStart = activityStart.toLocalDate().atTime(validStart);
        LocalDateTime codeEnd = activityEnd.toLocalDate().atTime(validEnd);

        // 间隔不能小于5分钟
        Assert.isTrue(validStart.plusMinutes(5).isBefore(validEnd), "签到码有效时间间隔不能小于5分钟");

        // 活动未开始不能生成签到码
        Assert.isFalse(codeStart.isAfter(LocalDateTime.now()), "活动未开始，不能生成签到码");

        // 生成签到码必须在活动时间段内
        Assert.isTrue(codeStart.isBefore(LocalDateTime.now()) && codeEnd.isAfter(LocalDateTime.now()), "签到码必须在活动时间段内");

        // 4. 生成唯一签到码
        String checkinCode = generateUniqueCode(activity.getId());

        CheckinCode code = new CheckinCode();
        code.setActivityId(activity.getId());
        code.setCode(checkinCode);
        code.setValidStart(codeAddBo.getValidStart());
        code.setValidEnd(codeAddBo.getValidEnd());
        code.setCreateId(currentUser.getId());
        code.setCreateTime(LocalDateTime.now());

        this.save(code);
        return checkinCode;
    }

    @Override
    public String generateUniqueCode(Long activityId) {
        int retryCount = 0;


        while (retryCount++ < MAX_RETRY) {
            // 方案1：UUID + 活动ID哈希
            String rawCode = activityId + "-" + UUID.randomUUID().toString();
            String code = DigestUtils.md5DigestAsHex(rawCode.getBytes()).substring(0, 8);

            // 检查唯一性
            if (existsByCode(activityId,code)) {
                return code;
            }
        }
        throw new CheckinCodeException("生成签到码失败，请重试");
    }

    @Override
    public boolean existsByCode(Long activityId, String code) {

        LambdaQueryWrapper<CheckinCode> queryWrapper =
                new QueryWrapper<CheckinCode>()
                        .lambda()
                        .eq(CheckinCode::getActivityId, activityId)
                        .eq(CheckinCode::getCode, code);
        CheckinCode checkinCode = this.getOne(queryWrapper);
        return checkinCode == null;
    }

    @Transactional
    @Override
    public void checkin(CheckinBo checkinBo) {

        CurrentUserBO currentUser = UserContext.getCurrentUser();
        Assert.notNull(currentUser, "用户未登入");

        Activity activity = activityService.getActivityById(checkinBo.getActivityId());
        Assert.notNull(activity, "活动不存在");

        CheckinCode checkinCode = this.getOne(
                new QueryWrapper<CheckinCode>()
                        .lambda()
                        .eq(CheckinCode::getActivityId, checkinBo.getActivityId())
                        .eq(CheckinCode::getCode, checkinBo.getCode())
        );
        Assert.notNull(checkinCode, "签到码不存在");

        LocalTime now = LocalTime.now();
        Assert.isTrue(checkinCode.getValidStart().isBefore(now) && checkinCode.getValidEnd().isAfter(now), "签到码已失效");
        Assert.isTrue(StrUtil.equals(checkinCode.getCode(), checkinBo.getCode()), "签到码不匹配");

        CheckinRecord record = checkinRecordService.selectByUserId(currentUser.getId(), checkinBo.getActivityId());
        Assert.isFalse(record.getStatus() == CheckinStatus.SIGNED, "您已签到");

        record.setStatus(CheckinStatus.SIGNED);
        checkinRecordService.updateStatus(record);



    }
}
